package com.ruoyi.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.common.constant.DeptConstants;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.helper.DataBaseHelper;
import com.ruoyi.common.helper.DataPermissionHelper;
import com.ruoyi.common.utils.StreamUtils;
import com.ruoyi.system.domain.SysRoleDept;
import com.ruoyi.system.mapper.SysDeptMapper;
import com.ruoyi.system.mapper.SysRoleDeptMapper;
import com.ruoyi.system.service.ISysDataScopeService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 数据权限 实现
 * <p>
 * 注意: 此Service内不允许调用标注`数据权限`注解的方法
 * 例如: deptMapper.selectList 此 selectList 方法标注了`数据权限`注解 会出现循环解析的问题
 *
 * @author Lion Li
 */
@RequiredArgsConstructor
@Service("sdss")
public class SysDataScopeServiceImpl implements ISysDataScopeService {

    private final SysRoleDeptMapper roleDeptMapper;
    private final SysDeptMapper deptMapper;

    @Override
    public String getRoleCustom(Long roleId) {
        List<SysRoleDept> list = roleDeptMapper.selectList(
                new LambdaQueryWrapper<SysRoleDept>()
                        .select(SysRoleDept::getDeptId)
                        .eq(SysRoleDept::getRoleId, roleId));
        if (CollUtil.isNotEmpty(list)) {
            return StreamUtils.join(list, rd -> Convert.toStr(rd.getDeptId()));
        }
        return null;
    }

    @Override
    public String getDeptAndChild(Long deptId) {
        List<SysDept> deptList = DataPermissionHelper.ignore(() -> deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                .select(SysDept::getDeptId)
                .apply(DataBaseHelper.findInSet(deptId, "ancestors")))
        );
        List<Long> ids = StreamUtils.toList(deptList, SysDept::getDeptId);
        ids.add(deptId);
        return StreamUtils.join(ids, Convert::toStr);
    }

    @Override
    public String getAccessFilterDeptAndChild(Long deptId) {
        Long accessFilterDeptId = getAccessFilterDeptId(deptId);
        return getDeptAndChild(accessFilterDeptId);
    }


    /**
     * 获取当前部门上级最近的主体责任部门
     *
     * @param currentDeptId 当前部门
     * @return 主体责任部门ID
     */
    @Override
    public Long getAccessFilterDeptId(Long currentDeptId) {
        return DataPermissionHelper.ignore(() -> {
            SysDept dept = deptMapper.selectById(currentDeptId);
            if (ObjectUtil.isNull(dept)) {
                throw new ServiceException("部门不存在：" + currentDeptId);
            }
            int grade = Integer.parseInt(dept.getGrade().substring(0, 1));
            String[] split = dept.getAncestors().split(",");

            if (ObjectUtil.equal(dept.getParentId(), DeptConstants.TOP_PARENT_ID) || (split.length == grade
                    && ObjUtil.equal(dept.getType(), DeptConstants.DEPT_TYPE_ORGANIZATION))) {
                return currentDeptId;
            }
            List<Long> deptIds = Arrays.stream(StrUtil.splitToLong(dept.getAncestors(), ",")).boxed().collect(Collectors.toList());

            SysDept sysDept = deptMapper.selectList(new LambdaQueryWrapper<SysDept>()
                            .eq(SysDept::getStatus, UserConstants.DEPT_NORMAL)
                            .eq(SysDept::getType, DeptConstants.DEPT_TYPE_ORGANIZATION)
                            .lt(SysDept::getLevel, 3)
                            .in(SysDept::getDeptId, deptIds)
                            .apply("`level` + 1 = LEFT(`grade`,1)")
                            .select(SysDept::getDeptId, SysDept::getAncestors))
                    .stream().max(Comparator.comparingInt(d -> d.getAncestors().length()))
                    .orElse(null);
            return ObjectUtil.isNotNull(sysDept) ? sysDept.getDeptId() : currentDeptId;
        });
    }

}
